package com.jamie.jvm.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 多个线程同时读一个资源类没有任何问题，所以为了满足并发量，读取共享资源应该同时发生。
 * 但是
 * 如果有一个线程想去写共享资源，就不应该再有其他线程对资源进行独活蟹
 * 小总结：
 * 读-读能共存
 * 读-写不能共存
 * 写-写不能共存
 *
 * 写操作：原子+原占，整个过程必须是一个完整的统一体，中间不许被分割，被打断
 * @author jamie
 */
public class ReadWriterLockDemo {

    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for(int i = 0; i < 5; i++){
            final int tempInt = i;
            new Thread(() -> myCache.put(tempInt + "", tempInt + ""), "Thread-" + i).start();
        }
        for(int i = 0; i < 5; i++){
            final int tempInt = i;
            new Thread(() -> myCache.get(tempInt + ""), "Thread-" + i).start();
        }
    }

}

/** 资源类 */
class MyCache {

    /** 缓存存储对象 */
    private volatile Map<String, Object> map = new HashMap<>();

    /** 互斥锁实例化 */
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    /**
     * 插入缓存
     * @param key   键
     * @param value 值
     */
    public void put(String key, Object value) {
        try{
            // 写入操作上锁
            rwLock.writeLock().lock();
            System.out.println(Thread.currentThread().getName() + "\t 正在写入：" + key);
            // 暂停一下线程
            threadSleep(300);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 正在完成");
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            // 写入操作解锁
            rwLock.writeLock().unlock();
        }

    }

    /**
     * 获取缓存
     * @param key 键
     */
    public Object get(String key) {
        Object result = null;
        try{
            // 读取操作上锁
            rwLock.readLock().lock();
            System.out.println(Thread.currentThread().getName() + "\t 正在读取");
            // 暂停一下线程
            threadSleep(300);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t 读取完成：" + result);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            // 读取操作解锁
            rwLock.readLock().unlock();
        }
        return result;
    }

    /**
     * 线程休眠
     * @param milliseconds 毫秒数
     */
    private void threadSleep(int milliseconds) {
        try{
            TimeUnit.MICROSECONDS.sleep(milliseconds);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }

}